#ifndef FILEMANAGER_H
#define FILEMANAGER_H
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <glm/glm.hpp>
#include "Utils.h"

class FileWriter // write file as bytes
{
public:

    FileWriter(const std::string &fileName){
        file.open(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
        if (!file.is_open())
            throw std::runtime_error("File not found");
        
    }
    ~FileWriter(){
        file.close();
    }
    void Write(const std::string &str){
        file.write(str.c_str(), str.size());
    }
    void Write(const char *str){
        file.write(str, strlen(str));
    }
    void Write(const char c){
        file.write(&c, 1);
    }
    void WriteFloat32(float f){
        file.write((char*)&f, sizeof(f));
    }
    void WriteInt32(int32_t i){
        file.write((char*)&i, sizeof(i));
    }
    void WriteUInt32(uint32_t i){
        file.write((char*)&i, sizeof(i));
    }
    void WriteQuat(const glm::quat &q){
        file.write((char*)&q, sizeof(q));
    }
    void WriteVec4(const glm::vec4 &v){
        file.write((char *)&v, sizeof(v));
    }
    void WriteVec3(const glm::vec3 &v){
        file.write((char*)&v, sizeof(v));
    }
    void WriteVec2(const glm::vec2 &v){
        file.write((char*)&v, sizeof(v));
    }

    void WriteBool(bool b){
        file.write((char*)&b, sizeof(b));
    }
    void Close(){
        file.close();
    }
private:
    std::ofstream file;

};

class FileReader // read file as bytes
{
public:
    static bool exists(const std::string &fileName){
        std::ifstream file(fileName);
        return file.good();
    }

    FileReader(const std::string &fileName){
        file.open(fileName, std::ios::binary);
    }
    ~FileReader(){
        file.close();
    }
    std::string ReadAll(){
        std::string str;
        char c;
        while(file.get(c)){
            str += c;
        }
        return str;
    }
    std::string ReadUntil(const char c = '?'){
        std::string str;
        char c2;
        while(file.get(c2) && c2 != c){
            str += c2;
        }
        return str;
    }
    std::string ReadString(int size){
        std::string str;
        char c;
        for(int i = 0; i < size; i++){
            file.get(c);
            str += c;
        }
        return str;
    }
    float ReadFloat32(){
        float f;
        file.read((char*)&f, sizeof(f));
        return f;
    }
    int32_t ReadInt32(){
        int32_t i;
        file.read((char*)&i, sizeof(i));
        return i;
    }
    uint32_t ReadUInt32()
    {
        uint32_t i;
        file.read((char *)&i, sizeof(i));
        return i;
    }
    glm::vec4 ReadVec4(){
        glm::vec4 v;
        file.read((char*)&v, sizeof(v));
        return v;
    }
    glm::vec3 ReadVec3(){
        glm::vec3 v;
        file.read((char*)&v, sizeof(v));
        return v;
    }
    glm::vec2 ReadVec2(){
        glm::vec2 v;
        file.read((char*)&v, sizeof(v));
        return v;
    }
    glm::quat ReadQuat(){
        glm::quat q;
        file.read((char*)&q, sizeof(q));
        return q;
    }

    bool ReadBool(){
        bool b;
        file.read((char*)&b, sizeof(b));
        return b;
    }
    void StrafePtr(int length){
        file.seekg(length, std::ios::cur);
    }
    void StrafePtr(int length, std::ios::seekdir dir){
        file.seekg(length, dir);
    }
    void ReturnToStart(){
        file.seekg(0, std::ios::beg);
    }
    void GoToEnd(){
        file.seekg(0, std::ios::end);
    }
    void Close()
    {
        file.close();
    }

private:
    std::ifstream file;
};

class YamlWriter
{
public:
    YamlWriter(const std::string &fileName)
    {
        file.open(fileName, std::ios::out | std::ios::trunc);
        if (!file.is_open())
            throw std::runtime_error("File not found");
    }
    ~YamlWriter()
    {
        file.close();
    }

    void WriteLine(const std::string &str)
    {
        file << str << std::endl;
    }

    void WriteLineAfterTab(const std::string &str)
    {
        file << "\t" << str << std::endl;
    }

    void WriteLineAfterTabs(const std::string &str, int tabs)
    {
        for (int i = 0; i < tabs; i++)
            file << "\t";
        file << str << std::endl;
    }

    void Write(const std::string &str)
    {
        file << str;
    }

    void WriteAfterTab(const std::string &str)
    {
        file << "\t" << str;
    }

    void WriteAfterTabs(const std::string &str, int tabs)
    {
        for (int i = 0; i < tabs; i++)
            file << "\t";
        file << str;
    }

    void WriteBoolEndLine(bool b)
    {
        file << b << std::endl;
    }

    void WriteFloatEndLine(float f)
    {
        file << f << std::endl;
    }

    void WriteIntEndLine(int i)
    {
        file << i << std::endl;
    }

    void WriteVec3EndLine(const glm::vec3 &v)
    {
        file << v.x << ", " << v.y << ", " << v.z << std::endl;
    }

    void WriteQuatEndLine(const glm::quat &q)
    {
        file << q.x << ", " << q.y << ", " << q.z << ", " << q.w << std::endl;
    }

    void WriteMat4(const std::string name, const glm::mat4 &m)
    {
        glm::vec3 scale = ExtractScale(m);
        WriteAfterTab(name + "Scale: ");
        WriteVec3EndLine(scale);
        WriteAfterTab(name + "Rotation: ");
        WriteQuatEndLine(ExtractQuaternion(ExtractRotationMatrix(m, scale)));
        WriteAfterTab(name + "Translation: ");
        WriteVec3EndLine(ExtractTranslation(m));
    }

    void Close()
    {
        file.close();
    }

private:
    std::ofstream file;
};

#endif // FILEMANAGER